﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using Microsoft.MediaCenter.Guide;
using System.Xml;
using Microsoft.MediaCenter.Store.MXF;
using Microsoft.MediaCenter.Store;
using System.Security.Cryptography;
using System.Threading;

namespace MCHelp
{
    public class MCConnection : IDisposable
    {
        private string User;
        private string Pass;
        private ObjectStore store;
        private ObjectStore store2;
        private Lineup mainLineup;
        //private Lineup _Tvm2W7Lineup;
        private Lineup _BroadbandLineup;
        private bool _defaultSingleton = false;
        public string lastError {get;set;}

        public MCConnection()
        {
            this.User = "Anonymous!User";
            this.Pass = this.GetProviderPass();
            this.GetObjectStore(this._defaultSingleton);
        }

        public MCConnection(bool defaultSingleton)
        {
            this._defaultSingleton = defaultSingleton;
            this.User = "Anonymous!User";
            this.Pass = this.GetProviderPass();
            this.GetObjectStore(this._defaultSingleton);
        }

        public Lineup BroadBandLineup
        {
            get
            {
                if (_BroadbandLineup == null)
                {
                    Lineups lineups = new Lineups(this.store);
                    _BroadbandLineup = lineups.WithUId("!MCLineup!Broadband");
                    if (_BroadbandLineup == null)
                    {
                        
                    }
                    if (_BroadbandLineup.LockCount <= 0)
                    {
                        _BroadbandLineup.Update(delegate
                        {
                            _BroadbandLineup.Lock();
                        });
                    }
                }
                return _BroadbandLineup;
            }

        }


        /// <summary>
        /// Get or Set how ObjectStore is retrieved :
        /// false : Default
        /// true : DefaultSingleton (like MC)
        /// </summary>
        public bool DefaultSingleton
        {
            get
            {
                return this._defaultSingleton;
            }
            set
            {
                this._defaultSingleton = value;
            }
        }

        /// <summary>
        /// Get the ObjectStore
        /// </summary>
        public ObjectStore Store
        {
            get
            {
                if (this.store == null)
                {
                    this.GetObjectStore(this._defaultSingleton);
                }
                return this.store;
            }
        }


        /// <summary>
        /// Get the ObjectStore
        /// </summary>
        public ObjectStore Store2
        {
            get
            {
                if (this.store2 == null)
                {
                    this.GetObjectStore2();
                }
                return this.store2;
            }
        }

       

        public bool DisposeObjectStore()
        {
            bool ret = false;
            if (this.store != null)
            {
                try
                {

                    this.store.Dispose();
                    this.store = null;
                    ret = true;
                }
                catch
                {
                    ret = false;
                }
            }
            if (this.store2 != null)
            {
                try
                {

                    this.store2.Dispose();
                    this.store2 = null;
                    ret = true;
                }
                catch
                {
                    ret = false;
                }
            }
            return ret;
        }

        public bool DisposeObjectStore(bool All)
        {
            bool ret = false;
            if (All)
            {
                try
                {
                    ObjectStore.DisposeAll();
                    ret = true;
                }
                catch
                {
                    ret = false;
                }
            }
            return ret;
        }


        

        /// <summary>
        /// Gets the Main Lineup
        /// </summary>
        public Lineup MainLineup
        {
            get
            {
                if (this.mainLineup == null)
                {
                    this.GetMainLineup();
                }
                return this.mainLineup;
            }
        }

        /// <summary>
        /// Gets the Tvm2W7 Lineup
        /// </summary>
        public Lineup Tvm2W7Lineup
        {
            get
            {
                return this.GetLocalLineup("Tvm2W7");
            }
        }

        /// <summary>
        /// Gets the MoreEPG Lineup
        /// </summary>
        public Lineup MoreEPGLineup
        {
            get
            {
                return this.GetLocalLineup("MoreEPGLineup");
            }
        }

        private Lineup GetLocalLineup(string Name)
        {
            try
            {
                return new Lineups(this.store).Where(s => s.Name == Name).Single();
            }
            catch
            {
                return null;
            }
        }



        /// <summary>
        /// Rename Channel with newName
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="newName"></param>
        /// <returns></returns>
        public bool RenameChannel(Channel channel, string newName)
        {
            Mutex mutex = null;
            try
            {
                mutex = Lineups.AcquireLineupLock(this.MainLineup);
                MergedChannel mergedChannel = channel as MergedChannel;

                if (mergedChannel != null)
                {
                    mergedChannel.Update(delegate
                    {
                        mergedChannel.CallSign = newName;
                    });
                }
                //this.mainLineup.RaiseLineupUpdatedEvent();
            }
            catch (Exception ex)
            {
                this.lastError = ex.Message;
                return false;
            }
            finally
            {
                Lineups.ReleaseLineupLock(mutex);
            }
            return true;
        }

        public bool SaveChannels(List<Channel> ChannelsToSave)
        {
            Mutex mutex = null;
            bool flag = false;
            bool ret = false;

            try
            {
                mutex = Lineups.AcquireLineupLock(this.MainLineup);
                foreach (Channel channel in ChannelsToSave)
                {
                    MergedChannel mergedChannel = channel as MergedChannel;

                    int number = mergedChannel.Number;
                    int subNumber = mergedChannel.SubNumber;
                    bool ignoreInband = mergedChannel.IgnoreInbandSchedule;
                    string callSign = mergedChannel.CallSign;
                    UserBlockedState userBlockedState = mergedChannel.UserBlockedState;

                    mergedChannel.Refresh(true);
                    if (mergedChannel.UserBlockedState != userBlockedState)
                    {
                        flag = true;
                    }
                    mergedChannel.Number = number;
                    mergedChannel.CallSign = callSign;
                    mergedChannel.SubNumber = subNumber;
                    mergedChannel.UserBlockedState = userBlockedState;
                    mergedChannel.IgnoreInbandSchedule = ignoreInband;
                    mergedChannel.Update();
                    if (flag)
                    {
                        foreach (TuningInfo info in mergedChannel.TuningInfos)
                        {
                            info.SetOverride(channel, userBlockedState, 0x7fffffff);
                        }
                    }
                }
                //this.mainLineup.RaiseLineupUpdatedEvent();
                ret = true;
            }
            catch (Exception ex)
            {
                this.lastError = ex.Message;
                ret = false;
            }
            finally
            {
                Lineups.ReleaseLineupLock(mutex);
            }
            return ret;
        }


        /// <summary>
        /// Block the Channel
        /// </summary>
        /// <param name="channel"></param>
        /// <returns></returns>
        public bool BlockOrUnblockChannel(Channel channel, bool block)
        {
            UserBlockedState state;
            Mutex mutex = null;
            if (block)
            {
                state = UserBlockedState.Blocked;
            }
            else
            {
                state = UserBlockedState.Enabled;
            }
            try
            {
                
                mutex = Lineups.AcquireLineupLock(this.MainLineup);
                channel.UserBlockedState = state;
                channel.Update();
                foreach (TuningInfo info in channel.TuningInfos)
                {
                    info.SetOverride(channel, state, 0x7fffffff);
                } 
                //this.mainLineup.RaiseLineupUpdatedEvent();
            }
            catch (Exception ex)
            {
                this.lastError = ex.Message;
                return false;
            }
            finally
            {
                Lineups.ReleaseLineupLock(mutex);
            }
            return true;
        }

        /// <summary>
        /// Sets the Inband using of Channel
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="InbandUsing"></param>
        /// <returns></returns>
        public bool ChangeInbandUsing(Channel channel, bool InbandUsing)
        {
            bool ret = false;
            Mutex mutex = null;
            MergedChannel mergedChannel = channel as MergedChannel;
            if (mergedChannel != null)
            {
                try
                {
                    mutex = Lineups.AcquireLineupLock(this.MainLineup);
                    mergedChannel.Update(delegate
                    {
                        mergedChannel.IgnoreInbandSchedule = InbandUsing;
                    });
                    //this.mainLineup.RaiseLineupUpdatedEvent();
                    ret = true;
                }
                catch (Exception ex)
                {
                    this.lastError = ex.Message;
                    ret = false;
                }
                finally
                {
                    Lineups.ReleaseLineupLock(mutex);
                }
            }
            return ret;
        }

        /// <summary>
        /// Export ObjectStore to MXF-File
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool ExportMxf(string path)
        {
            try
            {
                XmlWriter writer = XmlWriter.Create(path);
                MxfExporter.Export(this.store, writer, true);
                writer.Close();
                return true;
            }
            catch (Exception exception)
            {
                this.lastError = exception.Message;
                return false;
            }
        }


        /// <summary>
        /// Combine 2 Channels
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="guide"></param>
        /// <returns></returns>
        public bool CombineListingToChannel(Channel channel, Channel guide)
        {
            try
            {

                MergedChannel currentChannel = channel as MergedChannel;
                MergedChannel newListing = guide as MergedChannel;
                if ((currentChannel != null) && (newListing != null))
                {
                    currentChannel.CombineIntoChannel(newListing);
                }

                //this.mainLineup.RaiseLineupUpdatedEvent();
                this.store.FlushPendingExports();
                return true;
            }
            catch (Exception ex)
            {
                this.lastError = ex.Message;
                return false;
            }
        }

        /// <summary>
        /// Add Listing Channel. If Guide is Null, Channel is resetet to Default Listing
        /// </summary>
        /// <param name="channel"></param>
        /// <param name="guide"></param>
        /// <returns></returns>
        public bool AddListingToChannel(Channel channel, Channel guide)
        {
            try
            {
                this.store.FlushPendingExports();
                UpdateDelegate operation = null;

                MergedChannel mergedChannel = channel as MergedChannel;
                MergedChannel newListingMergedChannel = guide as MergedChannel;
                Channel chan = mergedChannel.GetRepresentativeSecondaryChannel(ChannelPromotion.PreferHighestDownloaded);

                if ((mergedChannel != null) && (newListingMergedChannel != null))
                {
                    try
                    {
                        Channel primaryChannel = newListingMergedChannel.PrimaryChannel;
                        mergedChannel.AddChannelListings(primaryChannel);
                        if (newListingMergedChannel.HasUserSpecifiedCallSign)
                        {
                            mergedChannel.Refresh(true);
                            if (operation == null)
                            {
                                operation = delegate
                                {
                                    mergedChannel.CallSign = newListingMergedChannel.CallSign;
                                };
                            }
                            mergedChannel.Update(operation);
                        }
                        //this.mainLineup.RaiseLineupUpdatedEvent();
                        this.store.FlushPendingExports();
                        return true;
                    }
                    catch (Exception ex)
                    {
                        this.lastError = ex.Message;
                        return false;
                    }
                }
                if ((mergedChannel != null) && (guide != null))
                {
                    try
                    {
                        mergedChannel.Refresh(true);
                        mergedChannel.AddChannelListings(guide);
                        //this.mainLineup.RaiseLineupUpdatedEvent();
                        this.store.FlushPendingExports();
                        return true;
                    }
                    catch (Exception ex)
                    {
                        this.lastError = ex.Message;
                        return false;
                    }
                }
                if ((mergedChannel != null) && (guide == null) && (chan != null))
                {
                    try
                    {
                        mergedChannel.Refresh(true);
                        mergedChannel.AddChannelListings(null);
                        //this.mainLineup.RaiseLineupUpdatedEvent();
                        this.store.FlushPendingExports();
                        return true;
                    }
                    catch (Exception ex)
                    {
                        this.lastError = ex.Message;
                        return false;
                    }
                }

                return false;
                
            }
            catch (Exception ex)
            {
                this.lastError = ex.Message;
                return false;
            }
        }

        /// <summary>
        /// Gets StoredObject from Id. Returns Type of T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="objectId"></param>
        /// <returns></returns>
        public T GetStoredObject<T>(long objectId)
        {

            StoredObject obj2 = null;
            T local = default(T);
            try
            {
                obj2 = this.store[objectId];
            }
            catch (Exception)
            {
                return local;
            }
            if (obj2 is T)
            {
                local = (T)Convert.ChangeType(obj2, typeof(T));
            }
            return local;
        }


        private Lineup GetMainLineup()
        {

            DeviceGroup group = new DeviceGroups(this.store).FindFirstEnabledDeviceGroup();
            if (group == null)
            {
                throw new InvalidOperationException("Enabled Device Group is Null.");
            }
            this.mainLineup = group.Lineup;
            return this.mainLineup;
        }

        private ObjectStore GetObjectStore(bool defaultSingleton)
        {
            //ObjectStore store = null;
            try
            {
                if (defaultSingleton)
                {
                    this.store = ObjectStore.DefaultSingleton;
                }
                else
                {
                    this.store = ObjectStore.Open(null, this.User, this.Pass, true);
                    //this.store = ObjectStore.Open(null, "Default", null, true);

                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("Error : Unable to get ObjectStore. " + ex.Message);
            }
            if (store == null)
            {
                throw new InvalidOperationException("ObjectStore is Null.");
            }
            return store;
        }

        private ObjectStore GetObjectStore2()
        {
            try
            {
                this.store2 = ObjectStore.Open(null, "Default", null, true);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("Error : Unable to get ObjectStore2. " + ex.Message);
            }
            if (store == null)
            {
                throw new InvalidOperationException("ObjectStore2 is Null.");
            }
            return store2;
        }



        private string GetProviderPass()
        {
            SHA256Managed managed = new SHA256Managed();
            byte[] bytes = Encoding.Unicode.GetBytes(this.GetEpgID());
            return Convert.ToBase64String(managed.ComputeHash(bytes));
        }

        private string GetEpgID()
        {
            string ret = null;

            RegistryKey localMachine = Registry.LocalMachine;
            if (localMachine == null)
            {
                throw new InvalidOperationException("Unable to access the registry.");
            }
            RegistryKey key2 = localMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Media Center\Service\EPG");
            try
            {
                if (key2 == null)
                {
                    throw new InvalidOperationException("Unable to get the subkey clientId root.");
                }
                ret = key2.GetValue("clientid") as string;
                if (ret == null)
                {
                    throw new InvalidOperationException("Unable to get the clientId content.");
                }
            }
            finally
            {
                if (key2 != null)
                {
                    key2.Close();
                }
            }
            return ret;
        }

        /// <summary>
        ///  Get Blocked Channels
        /// </summary>
        /// <returns></returns>
        public List<Channel> GetBlockedChannels()
        {
            MergedChannel item = null;
            List<Channel> list = new List<Channel>();
            StoredObjectsEnumerator<MergedChannel> storedObjectsEnumerator = new MergedChannels(this.Store).GetStoredObjectsEnumerator();
            while (storedObjectsEnumerator.MoveNext())
            {
                item = storedObjectsEnumerator.Current as MergedChannel;
                if (item == null)
                {
                    continue;
                }
                if (item.IsBlocked && item.Visibility == ChannelVisibility.Available && item.Lineup != null)
                {
                    list.Add(item);
                }
            }
            return list;
        }

        /// <summary>
        /// Get all Channels
        /// </summary>
        /// <returns></returns>
        public List<Channel> GetAllChannels()
        {
            MergedChannel item = null;
            List<Channel> list = new List<Channel>();
            StoredObjectsEnumerator<MergedChannel> storedObjectsEnumerator = new MergedChannels(this.Store).GetStoredObjectsEnumerator();
            while (storedObjectsEnumerator.MoveNext())
            {
                item = storedObjectsEnumerator.Current as MergedChannel;
                if (item == null)
                {
                    continue;
                }
                if (item.Visibility == ChannelVisibility.Available && item.Lineup != null)
                {
                    list.Add(item);
                }
            }
            return list;
        }

        /// <summary>
        /// Get Channels with Specified Type (TV, Radio...)
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public List<Channel> GetAllChannels(serviceType type)
        {
            MergedChannel item = null;
            List<Channel> list = new List<Channel>();
            StoredObjectsEnumerator<MergedChannel> storedObjectsEnumerator = new MergedChannels(this.Store).GetStoredObjectsEnumerator();
            while (storedObjectsEnumerator.MoveNext())
            {
                item = storedObjectsEnumerator.Current as MergedChannel;
                
                if (item == null)
                {
                    continue;
                }

                serviceType service = (serviceType)item.Service.ServiceType;
                if (service == type && item.Visibility == ChannelVisibility.Available && item.Lineup != null)
                {
                    list.Add(item);
                }
            }
            return list;
        }

        /// <summary>
        /// Get All Channels with Specified BlockState
        /// </summary>
        /// <param name="isBlocked"></param>
        /// <returns></returns>
        public List<Channel> GetAllChannels(bool isBlocked)
        {
            List<Channel> list = this.GetAllChannels().Where(s => s.IsBlocked == isBlocked).ToList();
            return list;
        }

        /// <summary>
        /// Get Channels with Specified Type (TV, Radio...) and Specified BlockState
        /// </summary>
        /// <param name="type"></param>
        /// <param name="isBlocked"></param>
        /// <returns></returns>
        public List<Channel> GetAllChannels(serviceType type, bool isBlocked)
        {
            List<Channel> list = this.GetAllChannels(type).Where(s => s.IsBlocked == isBlocked).ToList();
            return list;
        }

        #region IDisposable Member

        public void Dispose()
        {
            this.DisposeObjectStore();
        }

        #endregion
    }

    public enum serviceType : int
    {
        Unknown = 0,
        TV = 1,
        RADIO = 2,
        InteractiveTV = 3,
        IsdbBookmark = 4,
        IsdbEngineering = 5
    }
}
